using System;
using OracleInternal.Json.Sql;

namespace OracleInternal.Json.Binary
{
	internal abstract class OsonStructure : OracleJsonValue
	{
		protected OsonContext m_ctx;

		protected int m_pos;

		protected int m_size;

		protected byte m_childOffsetUb;

		protected int m_childArrayOffset;

		internal OsonStructure(OsonContext ctx)
		{
			m_ctx = ctx;
		}

		internal int Size()
		{
			return m_size;
		}

		internal bool IsEmpty()
		{
			return Size() == 0;
		}

		protected virtual void Init(int pos)
		{
			m_pos = pos;
		}

		protected void InitChildOffseUb(int op)
		{
			if (((uint)op & 0x20u) != 0)
			{
				m_childOffsetUb = 4;
			}
			else
			{
				m_childOffsetUb = 2;
			}
		}

		internal bool GetBooleanInternal(int offset)
		{
			int uB = m_ctx.m_osonBuffer.GetUB1(offset);
			if (uB < 0)
			{
				return false;
			}
			if (uB == 49)
			{
				return true;
			}
			_ = 50;
			return false;
		}

		internal string GetStringInternal(int offset)
		{
			int uB = m_ctx.m_osonBuffer.GetUB1(offset);
			if (uB < 0)
			{
				return null;
			}
			int charArrLen;
			if (uB <= 31)
			{
				m_ctx.m_osonBuffer.Position(offset + 1);
				return new string(m_ctx.m_osonBuffer.ReadString(uB, out charArrLen), 0, charArrLen);
			}
			switch (uB)
			{
			case 51:
				m_ctx.m_osonBuffer.GetUB1(offset + 1);
				m_ctx.m_osonBuffer.Position(offset + 2);
				return new string(m_ctx.m_osonBuffer.ReadString(uB, out charArrLen), 0, charArrLen);
			case 55:
				m_ctx.m_osonBuffer.GetUB2(offset + 1);
				m_ctx.m_osonBuffer.Position(offset + 3);
				return new string(m_ctx.m_osonBuffer.ReadString(uB, out charArrLen), 0, charArrLen);
			case 56:
				m_ctx.m_osonBuffer.GetUB4int(offset + 1);
				m_ctx.m_osonBuffer.Position(offset + 5);
				return new string(m_ctx.m_osonBuffer.ReadString(uB, out charArrLen), 0, charArrLen);
			default:
				return null;
			}
		}

		internal bool IsNullInternal(int childOffset)
		{
			int uB = m_ctx.m_osonBuffer.GetUB1(childOffset);
			if (uB < 0)
			{
				return false;
			}
			return uB == 48;
		}

		internal object GetValueInternal(int offset)
		{
			return GetValueInternal(offset, m_ctx.GetFactory(), m_ctx);
		}

		internal static object GetValueInternal(int offset, OracleOsonValueFactory factory, OsonContext ctx)
		{
			int uB = ctx.m_osonBuffer.GetUB1(offset);
			if ((uB & 0xC0) == 192)
			{
				return factory.CreateArray(ctx, offset);
			}
			if ((uB & 0xC0) == 128)
			{
				if ((uB & 0x83) == 131)
				{
					return GetOverflowValue(offset, factory, ctx);
				}
				return factory.CreateObject(ctx, offset);
			}
			if (uB <= 31)
			{
				return factory.CreateString(ctx, offset + 1, uB);
			}
			if (OsonConstants.IsSB4(uB))
			{
				return factory.CreateNumber(ReadRaw(offset + 1, uB & 7, ctx), TargetType.INT);
			}
			if (OsonConstants.IsSB8(uB))
			{
				return factory.CreateNumber(ReadRaw(offset + 1, uB & 0xF, ctx), TargetType.LONG);
			}
			if (OsonConstants.IsOraNum16(uB))
			{
				return factory.CreateNumber(ReadRaw(offset + 1, (uB & 0xF) + 1, ctx), TargetType.NONE);
			}
			if (OsonConstants.IsDec_16(uB))
			{
				return factory.CreateNumber(ReadRaw(offset + 1, (uB & 0xF) + 1, ctx), TargetType.DECIMAL);
			}
			switch (uB)
			{
			case 49:
				return factory.CreateTrue();
			case 50:
				return factory.CreateFalse();
			case 48:
				return factory.CreateNull();
			case 51:
			{
				int uB5 = ctx.m_osonBuffer.GetUB1(offset + 1);
				return factory.CreateString(ctx, offset + 2, uB5);
			}
			case 55:
			{
				int uB4 = ctx.m_osonBuffer.GetUB2(offset + 1);
				return factory.CreateString(ctx, offset + 3, uB4);
			}
			case 56:
			{
				int uB4int2 = ctx.m_osonBuffer.GetUB4int(offset + 1);
				ctx.m_osonBuffer.Position(offset + 5);
				return factory.CreateString(ctx, offset + 5, uB4int2);
			}
			case 54:
				ctx.m_osonBuffer.Position(offset + 1);
				return factory.CreateDouble(ctx.m_osonBuffer.ReadDtyDouble());
			case 127:
				ctx.m_osonBuffer.Position(offset + 1);
				return factory.CreateFloat(ctx.m_osonBuffer.ReadDtyFloat());
			case 53:
			{
				ctx.m_osonBuffer.GetUB1(offset + 1);
				ctx.m_osonBuffer.Position(offset + 2);
				int charArrLen;
				string value = new string(ctx.m_osonBuffer.ReadString(uB, out charArrLen), 0, charArrLen);
				return factory.CreateStringNumber(value);
			}
			case 52:
			{
				byte[] raw2 = ReadRaw(offset + 2, ctx.m_osonBuffer.GetUB1(offset + 1), ctx);
				return factory.CreateNumber(raw2, TargetType.NONE);
			}
			case 116:
			{
				byte[] raw = ReadRaw(offset + 2, ctx.m_osonBuffer.GetUB1(offset + 1), ctx);
				return factory.CreateNumber(raw, TargetType.DECIMAL);
			}
			case 126:
			{
				int uB3 = ctx.m_osonBuffer.GetUB1(offset + 1);
				if (uB3 > 127 || uB3 < 0)
				{
					throw new NotSupportedException(uB.ToString());
				}
				return factory.CreateBinary(ReadRaw(offset + 2, uB3, ctx), isId: true);
			}
			case 58:
			{
				int uB2 = ctx.m_osonBuffer.GetUB2(offset + 1);
				return factory.CreateBinary(ReadRaw(offset + 3, uB2, ctx), isId: false);
			}
			case 59:
			{
				int uB4int = ctx.m_osonBuffer.GetUB4int(offset + 1);
				return factory.CreateBinary(ReadRaw(offset + 5, uB4int, ctx), isId: false);
			}
			case 57:
			case 125:
				return factory.CreateTimestamp(ReadTimestamp(ctx.m_osonBuffer, offset));
			case 124:
				return factory.CreateTimestampTZ(ReadTimestampTZ(ctx.m_osonBuffer, offset));
			case 60:
				return factory.CreateDate(ReadRaw(offset + 1, 7, ctx));
			case 61:
				return factory.CreateIntervalYM(ReadRaw(offset + 1, 5, ctx));
			case 62:
				return factory.CreateIntervalDS(ReadRaw(offset + 1, 11, ctx));
			case 117:
				return GetOverflowValue(offset, factory, ctx);
			case 118:
				return GetForwardedValue(ctx.m_osonBuffer.GetUB2(offset + 1), factory, ctx);
			case 119:
				return GetForwardedValue(ctx.m_osonBuffer.GetUB4int(offset + 1), factory, ctx);
			default:
				throw new NotSupportedException(uB.ToString());
			}
		}

		private static object GetForwardedValue(int relativeOffset, OracleOsonValueFactory factory, OsonContext ctx)
		{
			return GetValueInternal(relativeOffset + ctx.m_osonHeader.extendedTreeSegmentOffset, factory, ctx);
		}

		private static object GetOverflowValue(int absoluteOffset, OracleOsonValueFactory factory, OsonContext ctx)
		{
			int key = absoluteOffset - ctx.m_osonHeader.treeSegmentOffset;
			OsonHeader osonHeader = ctx.m_osonHeader;
			return GetValueInternal(osonHeader.forwardingAddress[key] + osonHeader.extendedTreeSegmentOffset, factory, ctx);
		}

		private static byte[] ReadRaw(int offset, int len, OsonContext ctx)
		{
			ctx.m_osonBuffer.Position(offset);
			byte[] array = new byte[len];
			ctx.m_osonBuffer.Get(array);
			return array;
		}

		protected static byte[] ReadTimestamp(OsonBuffer b, int offset)
		{
			byte[] array = new byte[b.GetUB1(offset) switch
			{
				125 => OsonPrimitiveConversions.SIZE_TIMESTAMP_NOFRAC, 
				57 => OsonPrimitiveConversions.SIZE_TIMESTAMP, 
				_ => throw new InvalidCastException(), 
			}];
			b.Position(offset + 1);
			b.Get(array);
			return array;
		}

		protected static byte[] ReadTimestampTZ(OsonBuffer b, int offset)
		{
			if (b.GetUB1(offset) == 124)
			{
				int sIZE_TIMESTAMPTZ = OsonPrimitiveConversions.SIZE_TIMESTAMPTZ;
				byte[] array = new byte[sIZE_TIMESTAMPTZ];
				b.Position(offset + 1);
				b.Get(array);
				return array;
			}
			throw new InvalidCastException();
		}

		protected OsonAbstractArray GetArrayInternal(int childOffset)
		{
			if ((m_ctx.m_osonBuffer.GetUB1(childOffset) & 0xC0) == 192)
			{
				return m_ctx.GetFactory().CreateArray(m_ctx, childOffset);
			}
			throw new InvalidCastException();
		}

		protected OsonAbstractObject GetJsonObjectInternal(int childOffset)
		{
			if ((m_ctx.m_osonBuffer.GetUB1(childOffset) & 0xC0) == 128)
			{
				return m_ctx.GetFactory().CreateObject(m_ctx, childOffset);
			}
			throw new InvalidCastException();
		}

		protected abstract int GetChildOffset(int child);

		internal ByteBuffer GetBuffer()
		{
			ByteBuffer buffer = m_ctx.m_osonBuffer.m_buffer;
			buffer.Position(0);
			return buffer;
		}
	}
}
